<?php

namespace App\Services;

use App\Constant;
use App\Models\YeepayWallet;
use Carbon\Carbon;
use DateTime;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use YopClient3;
use YopRequest;
use YopSignUtils;

class YeepayService
{
    private $merchantNo;

    private $parentMerchantNo;
    private $secretKey;
    private $publicKey;
    private $payNotifyUrl;

    /**
     * YeepayService constructor.
     */
    public function __construct()
    {
        $this->parentMerchantNo = config('services.yeepay.merchant_pno');
        $this->merchantNo = $this->parentMerchantNo;
        $this->secretKey = config('services.yeepay.secret_key');
        $this->publicKey = config('services.yeepay.public_key');
        $this->payNotifyUrl = config('services.yeepay.pay_notify_url');
    }

    public function getMerchantNo()
    {
        return $this->merchantNo;
    }

    public function setMerchantNo($merchantNo): void
    {
        $this->merchantNo = $merchantNo;
    }

    /** 设置支付成功回调链接 */
    public function setPayNotifyUrl($payNotifyUrl)
    {
        $this->payNotifyUrl = $payNotifyUrl;
    }

    /** 获取聚合支付链接 */
    public function getPayLink($orderNo, $amount, $goodsName, DateTime $expireTime)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'orderId' => $orderNo,
            'orderAmount' => $amount,
            'scene' => 'OFFLINE',
            'fundProcessType' => 'DELAY_SETTLE',
            'expiredTime' => ($expireTime ?: now()->addMinutes(10))->toDateTimeString(),
            'notifyUrl' => $this->payNotifyUrl,
            'goodsName' => $goodsName,
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('GET_PAY_LINK', $request->paramMap);
        $resp = YopClient3::post('/rest/v1.0/aggpay/pay-link', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('GET_PAY_LINK_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('00000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception(json_encode($resp->error));
    }

    /** 获取支付宝扫码支付参数 */
    public function getAliQrpayParams($orderNo, $amount, $goodsName, DateTime $expireTime)
    {
        $resp = $this->prepay(
            $orderNo,
            $amount,
            $goodsName,
            'USER_SCAN',
            'ALIPAY',
            'OFFLINE',
            null,
            null,
            $expireTime
        );

        return Arr::get($resp, 'prePayTn');
    }

    /** 获取银联支付链接 */
    public function getUnionPayLink($orderNo, $amount, $goodsName, $userNo, $walletUserNo, $redirectUrl, DateTime $expireTime)
    {
        $result = $this->unionOrder($orderNo, $amount, $goodsName, $redirectUrl, $expireTime);

        $cashier = [
            'appKey' => $this->getAppKey(),
            'merchantNo' => $this->parentMerchantNo,
            'token' => $result['token'],
            'timestamp' => Carbon::now()->getTimestamp(),
            'directPayType' => '',
            'cardType' => '',
            'userNo' => $userNo,
            'userType' => 'USER_ID',
            'ext' => json_encode([
                'limitPayType' => 'YJZF',
            ]),
        ];
        if ($walletUserNo) {
            $cashier['ext'] = json_encode([
                'limitPayType' => 'WALLET_PAY',
                'walletMemberNo' => $walletUserNo,
            ]);
        }

        return 'https://cash.yeepay.com/cashier/std?' . $this->getUrlParam($cashier);
    }

    /** 查询订单信息 */
    public function queryOrder($orderId)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'orderId' => $orderId,
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('QUERY_ORDER', $request->paramMap);
        $resp = YopClient3::get('/rest/v1.0/trade/order/query', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('QUERY_ORDER_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('OPR00000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception(json_encode($resp->error));
    }

    /** 关闭订单 */
    public function closeOrder($orderId, $uniqueOrderNo)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'orderId' => $orderId,
            'uniqueOrderNo' => $uniqueOrderNo,
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('CLOSE_ORDER', $request->paramMap);
        $resp = YopClient3::post('/rest/v1.0/trade/order/close', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('CLOSE_ORDER_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('OPR00000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception(($resp->error->message . ':' . $resp->error->subMessage));
    }

    /** 分账请求 */
    public function divideApply($orderId, $uniqueOrderNo, array $divideDetails)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'orderId' => $orderId,
            'uniqueOrderNo' => $uniqueOrderNo,
            'divideRequestId' => 'divide_' . uniqid(),
            'divideDetail' => json_encode($divideDetails),
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('DIVIDE_APPLY', $request->paramMap);
        $resp = YopClient3::post('/rest/v1.0/divide/apply', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('DIVIDE_APPLY_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('OPR00000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception(json_encode($resp->error));
    }

    /** 上传文件 */
    public function upload($url)
    {
        $request = $this->getYopRequest();
        $request->addFile('merQual', $url);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('UPLOAD', ['url' => $url]);

        $resp = YopClient3::upload('/yos/v1.0/sys/merchant/qual/upload', $request);

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('UPLOAD_RESP', json_decode(json_encode($resp), true) ?: []);
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('REG00000' != $result['returnCode']) {
                throw new \Exception($result['returnMsg']);
            }

            return $result['merQualUrl'];
        }

        throw new \Exception($resp->error ? json_encode($resp->error) : '未知错误');
    }

    /** 小微商户入驻 */
    public function microRegister($params)
    {
        $request = $this->getYopRequest();
        $requestNo = 'micro_reg_' . uniqid();
        $request->paramMap = [
            'requestNo' => $requestNo,
            'businessRole' => 'SHARE_MERCHANT',
            'merchantSubjectInfo' => json_encode([
                'signName' => $params['real_name'],
                'shortName' => $params['real_name'],
            ]),
            'merchantCorporationInfo' => json_encode([
                'legalLicenceType' => 'ID_CARD',
                'legalLicenceNo' => $params['id_card'],
                'legalLicenceFrontUrl' => $params['id_card_front_image'],
                'legalLicenceBackUrl' => $params['id_card_back_image'],
                'mobile' => $params['bank_card_mobile'],
            ]),
            'businessAddressInfo' => json_encode([
                'province' => $params['province'],
                'city' => $params['city'],
                'district' => $params['district'],
                'address' => $params['address'],
            ]),
            'accountInfo' => json_encode([
                'bankAccountType' => 'DEBIT_CARD',
                'bankCardNo' => $params['bank_card_no'],
                'bankCode' => $params['bank_code'],
            ]),
            'notifyUrl' => config('services.yeepay.micro_register_notify_url'),
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('MICRO_REGISTER', $request->paramMap);
        $resp = YopClient3::post('/rest/v2.0/mer/register/contribute/micro', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('MICRO_REGISTER_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('NIG00000' != $result['returnCode']) {
                throw new \Exception($result['returnMsg']);
            }

            return $requestNo;
        }

        throw new \Exception(json_encode($resp->error));
    }

    /** 绑卡 */
    public function addMerchantCard($merchantNo, $bankCardNo, $bankCode)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $merchantNo,
            'bankCardNo' => $bankCardNo,
            'bankCardType' => 'DEBIT_CARD',
            'bankCode' => $bankCode,
            'defaultSettleCard' => true,
        ];
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('SETTLE_CARD', $request->paramMap);
        $resp = YopClient3::post('/rest/v1.0/settle/settle-card/add', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('SETTLE_CARD_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('000000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception($resp->error->message);
    }

    /** 数据解密 */
    public function decryptData($rawData)
    {
        $jsonStr = YopSignUtils::decrypt(
            $rawData,
            $this->secretKey,
            $this->publicKey
        );

        return json_decode($jsonStr, true);
    }

    /** 企业入驻 */
    public function enterpriseRegister($params)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'requestNo' => $params['requestNo'],
            'businessRole' => 'SHARE_MERCHANT',
            'merchantSubjectInfo' => json_encode([
                'licenceUrl' => $params['licenceUrl'],
                'signName' => $params['signName'],
                'signType' => $params['signType'],
                'licenceNo' => $params['licenceNo'],
                'shortName' => $params['shortName'],
            ]),
            'merchantCorporationInfo' => json_encode([
                'legalName' => $params['legalName'],
                'legalLicenceType' => $params['legalLicenceType'],
                'legalLicenceNo' => $params['legalLicenceNo'],
                'legalLicenceFrontUrl' => $params['legalLicenceFrontUrl'],
                'legalLicenceBackUrl' => $params['legalLicenceBackUrl'],
            ]),
            'merchantContactInfo' => json_encode([
                'contactName' => $params['contactName'],
                'contactMobile' => $params['contactMobile'],
                'contactEmail' => $params['contactEmail'],
                'contactLicenceNo' => $params['contactLicenceNo'],
                'servicePhone' => $params['servicePhone'],
            ]),
            'businessAddressInfo' => json_encode([
                'province' => $params['province'],
                'city' => $params['city'],
                'district' => $params['district'],
                'address' => $params['address'],
            ]),
            'settlementAccountInfo' => json_encode([
                'settlementDirection' => 'BANKCARD',
                'bankCode' => $params['bankCode'],
                'bankAccountType' => $params['bankAccountType'],
                'bankCardNo' => $params['bankCardNo'],
            ]),
            'notifyUrl' => config('services.yeepay.enterprise_register_notify_url'),
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('ENTERPRISE_REGISTER', $request->paramMap);
        $resp = YopClient3::post('/rest/v2.0/mer/register/contribute/merchant', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('MICRO_REGISTER_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            Log::error('enterprise_result:' . json_encode($result));
            if ('NIG00000' != $result['returnCode']) {
                throw new \Exception($result['returnMsg']);
            }

            return $result;
        }

        throw new \Exception(json_encode($resp->error));
    }

    /** 获取钱包登录/注册地址 */
    public function getWalletAddress(YeepayWallet $wallet, $returnUrl)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'merchantUserNo' => $wallet->merchant_user_no,
            'name' => $wallet->name,
            'mobile' => $wallet->mobile,
            'certificateType' => 'IDENTITY_CARD',
            'certificateNo' => $wallet->id_card_no,
            'returnUrl' => $returnUrl,
            'notifyUrl' => config('services.yeepay.open_wallet_notify_url'),
            'requestNo' => $wallet->merchant_user_no . '_' . uniqid(),
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('GET_WALLET_ADDRESS', $request->paramMap);
        $resp = YopClient3::post('/rest/v2.0/m-wallet/wallet/index', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('GET_WALLET_ADDRESS_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            return json_decode(json_encode($resp->result), true);
        }

        throw new \Exception(json_encode($resp->error));
    }

    /** 预支付 */
    private function prepay($orderNo, $amount, $goodsName, $payWay, $channel, $scene, $appId = null, $userId = null, DateTime $expireTime)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'orderId' => $orderNo,
            'orderAmount' => $amount,
            'scene' => $scene,
            'fundProcessType' => 'DELAY_SETTLE',
            'expiredTime' => ($expireTime ?: now()->addMinutes(10))->toDateTimeString(),
            'notifyUrl' => $this->payNotifyUrl,
            'userIp' => '127.0.0.1',
            'goodsName' => $goodsName,
            'payWay' => $payWay,
            'channel' => $channel,
            'appId' => $appId,
            'userId' => $userId,
        ];

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('PREPAY', $request->paramMap);
        $resp = YopClient3::post('/rest/v1.0/aggpay/pre-pay', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('PREPAY_RESP', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('00000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception(json_encode($resp->error));
    }

    private function getAppKey()
    {
        return 'app_' . $this->parentMerchantNo;
    }

    private function getYopRequest()
    {
        return new YopRequest(
            $this->getAppKey(),
            $this->secretKey
        );
    }

    private function unionOrder($orderNo, $amount, $goodsName, $redirectUrl, DateTime $expireTime)
    {
        $request = $this->getYopRequest();
        $request->paramMap = [
            'parentMerchantNo' => $this->parentMerchantNo,
            'merchantNo' => $this->merchantNo,
            'orderId' => $orderNo,
            'orderAmount' => $amount,
            'notifyUrl' => $this->payNotifyUrl,
            'expiredTime' => ($expireTime ?: Carbon::now()->addMinutes(10))->toDateTimeString(),
            'goodsName' => $goodsName,
            'fundProcessType' => 'DELAY_SETTLE',
        ];
        if ($redirectUrl) {
            $request->addParam('redirectUrl', $redirectUrl);
        }

        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('UNION_PAY', $request->paramMap);
        $resp = YopClient3::post('/rest/v1.0/trade/order', $request);
        \Log::channel(Constant::LOG_CHANNEL_YEEPAY)->info('UNION_PAY_RESPONSE', json_decode(json_encode($resp), true));
        if ('SUCCESS' === $resp->state) {
            $result = json_decode(json_encode($resp->result), true);
            if ('OPR00000' != $result['code']) {
                throw new \Exception($result['message']);
            }

            return $result;
        }

        throw new \Exception(json_encode($resp->error));
    }

    private function getUrlParam($params)
    {
        $queryKeyValues = [];
        foreach ($params as $k => $v) {
            $queryKeyValues[] = $k . '=' . $v;
        }
        $sign = \YopSignUtils::signRsa(join('&', $queryKeyValues), $this->secretKey);

        return join('&', $queryKeyValues) . '&sign=' . $sign;
    }
}
